#if !defined(LOG_TAG)
    #define LOG_TAG                    "uconfig"
#endif
#undef LOG_LVL
#if defined(UCONFIG_LOG_LVL)
    #define LOG_LVL                    UCONFIG_LOG_LVL
#endif

#include "uconfig.h"

#define AREA_FIRST 0
#define MIN_RESERVED_TIME_FIRST 1

static MACRO_STR_T bool_str[] = {
    MACRO_STR(true),
    MACRO_STR(false),
    {-1, NULL}
};

static int32_t uconfig_show_global_config(struct uconfig *puconfig)
{
    int32_t ret = 0;

    log_i("global config:");
   
    return ret;
}

static int32_t uconfig_default_param(struct uconfig *puconfig)
{
    int32_t ret = 0;
    return ret;
}

static cJSON *uconfig_json_create(void)
{
    cJSON *root, *node;

    root = cJSON_CreateObject();
    if (!root) {
        log_e("Error before: [%s]",cJSON_GetErrorPtr());

        return NULL;
    }
    node = cJSON_GetObjectItem(root, "version");
    if(!node){
        node = cJSON_CreateObject();
    }
    if(node){
        CJSON_SET_STRING(root, version,    "1.1");
    }

    return root;
}

static int32_t uconfig_json_save(struct uconfig *puconfig, cJSON *root)
{
    int ret = 0;
    char *buf;
    int cfg_size = 0;

    int fd;
    char *buffer;
    int size = 0x80000;

    buffer = (char*)malloc(size);
    if(!buffer){
        return -ENOMEM;
    }

    buf = cJSON_Print(root);
    if(buf){
        cfg_size = strlen(buf);
        memcpy(buffer, buf, cfg_size);
        free(buf);
    }
        
    cJSON_Delete(root);

    if(puconfig->default_config_file){
        fd = open(puconfig->default_config_file, O_CREAT | O_RDWR | O_TRUNC, 0655);
    }else{
        fd = open("config.json", O_CREAT | O_RDWR | O_TRUNC, 0655);
    }
    if(fd > 0){
        ret = write(fd, buffer, cfg_size);
        if(ret < 0){
            log_e("write %s error", puconfig->default_config_file);
        }
        close(fd);
    }

    free(buffer);
    buffer = NULL;

    return 0;
}

static int32_t uconfig_load_array(cJSON *root, char *obj, cJSON **array)
{
    *array = cJSON_GetObjectItem(root, obj);
    if(!(*array))
        return 0;

    return cJSON_GetArraySize(*array);
}

static int32_t uconfig_adjust_param(struct uconfig_info *info)
{
    int32_t ret = 0;
    log_d("now enter uconfig_adjust_param!!!!!!!");
    return ret;
}

static int32_t uconfig_load_config_file(struct uconfig *puconfig)
{
    int fd;

    if(!puconfig->input_config_file){
        log_e("No input config file, use default config file");
        puconfig->input_config_file = puconfig->default_config_file;
    }

    fd = open(puconfig->input_config_file, O_RDONLY, 0655);
    if(fd > 0){
        char *buffer;
        int size = 0x80000;
        
        buffer = (char*)malloc(size);
        if(!buffer){
            return -ENOMEM;
        }
        size = read(fd, buffer, size);

        cJSON *root;
        cJSON *array = NULL;
        uint32_t count = 0;
        int i;

        root = cJSON_Parse(buffer);
        if (!root) {
            log_e("Error before: [%s]",cJSON_GetErrorPtr());
            log_i("Load config file failed, use default config");
            uconfig_default_param(puconfig);
            puconfig->op->save(puconfig);
            return 0;
        }

        /*load sys parameter*/
        
        uconfig_adjust_param(&puconfig->info);
            
        cJSON_Delete(root);

        close(fd);
        free(buffer);
        buffer = NULL;
    }else{
        log_i("can't open default config file, create default");
        uconfig_default_param(puconfig);
    }

    puconfig->op->save(puconfig);

    return 0;
}

static int32_t uconfig_save_config_file(struct uconfig *puconfig)
{
    cJSON *root;
    int32_t ret;
    cJSON *array = NULL;
    cJSON * item = NULL;
    int i; 

    root = uconfig_json_create();
    if(!root)
        return -ENOMEM;

    uconfig_adjust_param(&puconfig->info);

    /*set sys parameter*/
    

    ret = uconfig_json_save(puconfig, root);
    if(ret != 0)
        return ret;

    return 0;
}

static int32_t uconfig_start(struct uconfig* puconfig)
{
    int32_t ret = 0;
    return ret;
}

static int32_t uconfig_stop(struct uconfig* puconfig)
{
    int32_t ret = 0;
    return ret;
}

static int32_t uconfig_init(struct uconfig* puconfig)
{
    int32_t ret = 0;

    INIT_LIST_HEAD(&(puconfig->head));
    lock_init(&(puconfig->lock));
    lock_init(&(puconfig->notify_lock));
    wait_init(&(puconfig->wait));

	puconfig->input_config_file = NULL;
    puconfig->default_config_file = "/software/config.json";
    
    return ret;
}

static int32_t uconfig_release(struct uconfig* puconfig)
{
    /*
    free all sources
    ......
    */

    lock_destroy((&puconfig->notify_lock));
    lock_destroy((&puconfig->lock));
    wait_destroy((&puconfig->wait));

    return 0;
}

static int32_t uconfig_register_notify(struct uconfig* puconfig, int32_t event, uconfig_event_notify notify, void* object)
{
    struct uconfig_event_action* paction;
    if(!notify || (event <= UCONFIG_EVENT_NONE) || (event >= UCONFIG_EVENT_MAX)){
        return -EINVAL;
    }
    paction = (struct uconfig_event_action*)malloc(sizeof(struct uconfig_event_action));
    if(!paction){
        log_e("malloc error");
        return -ENOMEM;
    }
    paction->notify = notify;
    paction->object = object;
    lock(&(puconfig->notify_lock));
    paction->next = puconfig->paction[event];
    puconfig->paction[event] = paction;
    unlock(&(puconfig->notify_lock));
    return 0;
}

static int32_t uconfig_unregister_notify(struct uconfig* puconfig, int32_t event, void* object)
{
    struct uconfig_event_action *paction,* ptmp;
    if((event <= UCONFIG_EVENT_NONE) || (event >= UCONFIG_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(puconfig->notify_lock));
    paction = puconfig->paction[event];
    if(paction){
        if(paction->object == object){
            puconfig->paction[event] = paction->next;
            free(paction);
        }else{
            while(paction->next){
                if(paction->next->object == object){
                    ptmp = paction->next;
                    paction->next = ptmp->next;
                    free(ptmp);
                    break;
                }
                paction = paction->next;
            }
        }
    }
    unlock(&(puconfig->notify_lock));

    return 0;
}

static int32_t uconfig_trigger_notify(struct uconfig* puconfig, int32_t event, void* context)
{
    struct uconfig_event_action* paction;
    if((event <= UCONFIG_EVENT_NONE) || (event >= UCONFIG_EVENT_MAX)){
        return -EINVAL;
    }
    lock(&(puconfig->notify_lock));
    paction = puconfig->paction[event];
    while(paction){
        paction->notify(puconfig, event, paction->object, context);
        paction = paction->next;
    }
    unlock(&(puconfig->notify_lock));

    return 0;
}

static struct uconfig_operation uconfig_op =
{
    .init = uconfig_init,
    .release = uconfig_release,
    
    .start = uconfig_start,
    .stop = uconfig_stop,
    
    .default_param = uconfig_default_param,
    .show = uconfig_show_global_config,
    .load = uconfig_load_config_file,
    .save = uconfig_save_config_file,

    .register_notify = uconfig_register_notify,
    .unregister_notify = uconfig_unregister_notify,
    .trigger_notify = uconfig_trigger_notify,
};

int32_t create_init_uconfig(struct uconfig** puconfig)
{
    int32_t ret;
    struct uconfig* ptmp;
    (*puconfig) = (struct uconfig*)malloc(sizeof(struct uconfig));
    if(!(*puconfig)){
        log_e("malloc error");
        return -ENOMEM;
    }
    ptmp = *puconfig;
    memset(ptmp, 0, sizeof(struct uconfig));
    ptmp->op = &uconfig_op;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        log_e("init error");
        release_destroy_uconfig(ptmp);
        return ret;
    }
    return 0;
}

void release_destroy_uconfig(struct uconfig* puconfig)
{
    if(puconfig){
        puconfig->op->release(puconfig);
        free(puconfig);
    }
}
        
